/// 一个 I/O 项目：构建一个命令行程序
// 创建一个我们自己版本的经典命令行工具：grep

/// 接受命令行参数
// cargo new minigrep
// 为了确保 minigrep 能够获取传递给它的命令行参数的值，我们需要一个 Rust 标准库提供的函数，
// 也就是 std::env::args。这个函数返回一个传递给程序的命令行参数的 迭代器（iterator）
// 迭代器生成一系列的值，可以在迭代器上调用 collect 方法将其转换为一个集合，比如包含所有迭代器产生元素的 vector。
use std::env;
use std::fs;

fn main() {
    /// 1、读取参数值
    // 将命令参数收集为一个 vector 集合
    let args: Vec<String> = env::args().collect();
    println!("{:?}", args);

    // 逐个打印命令行参数
    for argument in env::args() {
        println!("{argument}");
    }

    /// 2、将参数值保存进变量
    let query = &args[1];
    let filename = &args[2];

    println!("Searching for {}", query);
    println!("In file {}", filename);

    /// 3、读取文件
    let contents = fs::read_to_string(filename)
        .expect("Something went wrong reading the file");

    println!("With text:\n{}", contents);
}

/// 4、重构改进模块性和错误处理
/*
二进制项目的关注分离
main 函数负责多个任务的组织问题在许多二进制项目中很常见。所以 Rust 社区开发出一类在 main 函数开始变得庞大时进行二进制程序的关注分离的指导性过程。这些过程有如下步骤：
    将程序拆分成 main.rs 和 lib.rs 并将程序的逻辑放入 lib.rs 中。
    当命令行解析逻辑比较小时，可以保留在 main.rs 中。
    当命令行解析开始变得复杂时，也同样将其从 main.rs 提取到 lib.rs 中。
经过这些过程之后保留在 main 函数中的责任应该被限制为：
    使用参数值调用命令行解析逻辑
    设置任何其他的配置
    调用 lib.rs 中的 run 函数
    如果 run 返回错误，则处理这个错误
这个模式的一切就是为了关注分离：main.rs 处理程序运行，而 lib.rs 处理所有的真正的任务逻辑。因为不能直接测试 main 函数，这个结构通过将所有的程序逻辑移动到 lib.rs 的函数中使得我们可以测试他们。仅仅保留在 main.rs 中的代码将足够小以便阅读就可以验证其正确性。让我们遵循这些步骤来重构程序。
*/
use std::env;
use std::fs;

struct Config {
    query: String,
    filename: String,
}
impl Config {
    fn new(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("not enough arguments");
        }

        let query = args[1].clone();
        let filename = args[2].clone();

        Ok(Config { query, filename })
    }
}

fn parse_config(args: &[String]) -> Config {
    let query = args[1].clone();
    let filename = args[2].clone();

    Config { query, filename }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    // Config::new 调用并处理错误
    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });

    println!("Searching for {}", config.query);
    println!("In file {}", config.filename);



    let contents = fs::read_to_string(config.filename)
        .expect("Something went wrong reading the file");

    // --snip--
}

//
fn run(config: Config) {
    let contents = fs::read_to_string(config.filename)
        .expect("Something went wrong reading the file");

    println!("With text:\n{}", contents);
}